package com.derekandbritt.booST.server;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.derekandbritt.booST.data.EnergyHistory;
import com.derekandbritt.booST.data.EnergyLevel;

public class EnergyCalculator {

    private static final long ENERGY_EXPIRATION = 259200000; // 3 days
    private static final long ONE_THIRD_EXPIRATION = (ENERGY_EXPIRATION * 1) / 3;
    private static final long TWO_THIRD_EXPIRATION = (ENERGY_EXPIRATION * 2) / 3;
    
    public static EnergyLevel computeCurrentEnergyLevel(String userId) {
        return computeEnergyLevel(userId, 0);
    }
    
    @SuppressWarnings("unchecked")
    private static EnergyHistory computeEnergyLevel(String userId, long currentTime) {
        
        PersistenceManager pm = DataManager.getPersistenceManager();
        
        try { 
            List<EnergyLevel> result;
            
            final Query query = pm.newQuery(EnergyLevel.class);
            query.setOrdering("timestamp desc");
            query.declareImports("import java.util.Date");
            if(currentTime <= 0) {
                query.setFilter("userId == p_userId && timestamp > p_expiration");
                query.declareParameters("String p_userId, Date p_expiration");
            
                currentTime = System.currentTimeMillis();
                Date expiration = new Date(currentTime - ENERGY_EXPIRATION);
            
                result = (List<EnergyLevel>) query.execute(userId, expiration);
            }
            else {
                query.setFilter("userId == p_userId && timestamp > p_expiration && timestamp < p_currentTime");
                query.declareParameters("String p_userId, Date p_expiration, Date p_currentTime");
            
                Date expiration = new Date(currentTime - ENERGY_EXPIRATION);
                Date currentDate = new Date(currentTime);
            
                result = (List<EnergyLevel>) query.execute(userId, expiration, currentDate);
            }
            
            if(result == null || result.size() == 0)
                return null;
            
            double energyValue = 0;
            
            long oneThird = currentTime - ONE_THIRD_EXPIRATION;
            long twoThirds = currentTime - TWO_THIRD_EXPIRATION;
            
            for(EnergyLevel level : result) {
                long time = level.getTimestamp().getTime();
                if(time > oneThird)
                    energyValue += level.getLevel();
                else if(time > twoThirds)
                    energyValue += level.getLevel() * (2/3);
                else
                    energyValue += level.getLevel() * (1/3); 
            }
                
            EnergyHistory energyLevel = new EnergyHistory();
            energyLevel.setUserId(userId);
            energyLevel.setLevel(energyValue);
            energyLevel.setTimestamp(new Date(currentTime));
            
            return energyLevel;
        } finally {
            pm.close();
        }
    }
    
    @SuppressWarnings("unchecked")
    public static List<EnergyLevel> computePastEnergyLevels(String userId, int count) {
        
        PersistenceManager pm = DataManager.getPersistenceManager();
        try { 
            
            final Query query = pm.newQuery(EnergyHistory.class);
            query.setFilter("userId == p_userId");
            query.setOrdering("timestamp desc");
            query.declareParameters("String p_userId");
            query.setRange(0, count);
            List<EnergyLevel> result = (List<EnergyLevel>) query.execute(userId);
            
            long lastInterval = CalendarUtil.getLastInterval(); 
            
            // if none exist
            if(result.size() == 0) {
                EnergyHistory energy = computeEnergyLevel(userId, lastInterval);
                if(energy == null)
                    return null;
                pm.makePersistent(energy);
                result = new ArrayList<EnergyLevel>(1);
                result.add(energy);
                return result;
            }
            
            //convert the result into a mutable list
            pm.makeTransientAll(result);
            result = new ArrayList<EnergyLevel>(result);
            
            // check to see if the interval has been exceeded
            long lastComputedTime = result.get(0).getTimestamp().getTime();
            // make sure we back fill all the potentially missing values
            while(lastComputedTime < lastInterval) {

                // move the counter forward 12 hours
                lastComputedTime = lastComputedTime + 43200000; 
                
                EnergyHistory energy = computeEnergyLevel(userId, lastComputedTime);
                if(energy == null) {
                    // store an energy level of 0
                    energy = new EnergyHistory();
                    energy.setUserId(userId);
                    energy.setLevel(0);
                    energy.setTimestamp(new Date(lastComputedTime));
                }
                pm.makePersistent(energy);
                result.add(0, energy);
            }
            
            if(result.size() < count)
                return result;
            else
                return result.subList(0, (count - 1));

        } finally {
            pm.close();
        }
    }
}
